// questionController.js - 题目控制器
const Question = require('../models/question');
const asyncHandler = require('express-async-handler');
// xss 库净化
const xss = require('xss');
// 引入题目验证器
const { validateQuestionRequestBody } = require('../validators/questionValidator');

/**
 * @swagger
 * /api/questions:
 *   get:
 *     summary: 获取题目列表
 *     description: 检索题目列表，支持分页、按类型、难度、标签和内容搜索
 *     tags: [Questions]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *           default: 1
 *         description: 页码
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 10
 *         description: 每页数量
 *       - in: query
 *         name: type
 *         schema:
 *           type: string
 *         description: 题目类型
 *       - in: query
 *         name: difficulty
 *         schema:
 *           type: string
 *         description: 难度级别
 *       - in: query
 *         name: tag
 *         schema:
 *           type: string
 *         description: 标签
 *       - in: query
 *         name: search
 *         schema:
 *           type: string
 *         description: 搜索关键词
 *     responses:
 *       200:
 *         description: 成功获取题目列表
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 data:
 *                   type: object
 *                   properties:
 *                     questions:
 *                       type: array
 *                       items:
 *                         $ref: '#/components/schemas/Question'
 *                     pagination:
 *                       type: object
 *                       properties:
 *                         total:
 *                           type: integer
 *                         page:
 *                           type: integer
 *                         limit:
 *                           type: integer
 *                         pages:
 *                           type: integer
 *       500:
 *         description: 服务器错误
 */

exports.getAllQuestions = asyncHandler(async (req, res) => {
  // console.log('getAllQuestions');
  // 解析查询参数
  /**
   * const { page = 1, limit = 10, type, difficulty, tag, search } = req.query;
   * 这叫解构赋值
   * 相当于
   * const page = req.query.page !== undefined ? req.query.page : 1;
   * const limit = req.query.limit !== undefined ? req.query.limit : 10;
   * const type = req.query.type;
   * const difficulty = req.query.difficulty;
   * const tag = req.query.tag;
   * const search = req.query.search;
   */
  const { page = 1, limit = 10, type, difficulty, tag, search } = req.query;

  // 构建查询条件
  const query = {};

  // 按题目类型筛选
  if (type) query.type = type;
  // 按难度筛选
  if (difficulty) query.difficulty = difficulty;
  // 按标签筛选
  if (tag) query.tags = tag;
  // 按内容搜索（同时搜索文本和富文本内容）
  if (search) {
    query.$or = [
      { content: { $regex: search, $options: 'i' } },
      { htmlContent: { $regex: search, $options: 'i' } }
    ];
  }

  // 分页参数处理
  const currentPage = parseInt(page, 10); //将page转为10进制
  const pageSize = parseInt(limit, 10);   //将limit转为10进制
  //countDocuments 是 MongoDB 的方法，用于统计满足 query 条件的文档总数
  const total = await Question.countDocuments(query);
  //Math.ceil() 表示向上取整；用于计算共有多少页
  const maxPage = Math.ceil(total / pageSize);

  // 校验页码有效性
  if (currentPage < 1 || currentPage > maxPage) {
    res.status(400);
    throw new Error(`请求页码超出范围，最大页码为 ${maxPage}`);
  }

  /**
   * if (currentPage < 1 || currentPage > maxPage) {
        return res.status(400).json({
            success: false,
            message: `请求页码超出范围，最大页码为 ${maxPage}`
        });
      }
   */
  // 分页选项
  //Question.find(query) 返回的是一个 Mongoose Query 对象，可以链式调用 .limit(), .skip(), .sort() 等方法
  const questions = await Question.find(query)
    .limit(pageSize)
    .skip((currentPage - 1) * pageSize)
    .sort({ createdAt: -1 });

  res.status(200).json({
    success: true,
    data: {
      questions,
      pagination: {
        total,
        page: currentPage,
        limit: pageSize,
        pages: Math.ceil(total / pageSize)
      }
    }
  });
});

/**
 * @swagger
 * /api/questions/{id}:
 *   get:
 *     summary: 获取题目详情
 *     description: 获取单个题目的详细信息
 *     tags: [Questions]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: 题目ID
 *     responses:
 *       200:
 *         description: 成功获取题目
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 data:
 *                   $ref: '#/components/schemas/Question'
 *       404:
 *         description: 题目不存在
 *       500:
 *         description: 服务器错误
 */
exports.getQuestion = asyncHandler(async (req, res) => {
  const { id } = req.params;

  if (!id || !mongoose.Types.ObjectId.isValid(id)) {
    res.status(400);
    throw new Error('无效的类型ID');
  }

  const question = await Question.findById(req.params.id);
  if (!question) {
    res.status(404);
    throw new Error('题目不存在');
  }
  res.status(200).json({ success: true, data: question });
});

/**
 * @swagger
 * /api/questions:
 *   post:
 *     summary: 创建新题目
 *     description: 创建新题目，支持文本、富文本和图片题目
 *     tags: [Questions]
 *     requestBody:
 *       required: true
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             required:
 *               - type
 *               - source
 *             properties:
 *               content:
 *                 type: string
 *                 description: 文本内容
 *               htmlContent:
 *                 type: string
 *                 description: 富文本内容
 *               image:
 *                 type: string
 *                 format: binary
 *                 description: 图片文件
 *               type:
 *                 type: string
 *                 description: 题目类型ID
 *               options:
 *                 type: array
 *                 items:
 *                   type: object
 *                   properties:
 *                     label:
 *                       type: string
 *                     content:
 *                       type: string
 *                     htmlContent:
 *                       type: string
 *                     image:
 *                       type: string
 *                       format: binary
 *                     isCorrect:
 *                       type: boolean
 *               answer:
 *                 type: array
 *                 items:
 *                   type: string
 *                 description: 答案
 *               htmlAnswer:
 *                 type: string
 *                 description: 富文本答案
 *               imageAnswer:
 *                 type: string
 *                 description: 图片答案
 *               points:
 *                 type: number
 *                 description: 分值
 *               difficulty:
 *                 type: number
 *                 description: 难度(1-5)
 *               tags:
 *                 type: array
 *                 items:
 *                   type: string
 *               source:
 *                 type: array
 *                 items:
 *                   type: string
 *                 description: 题目来源ID数组
 *     responses:
 *       201:
 *         description: 题目创建成功
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Question'
 *       400:
 *         description: 参数错误
 *       409:
 *         description: 字段冲突
 *       500:
 *         description: 服务器错误
 */


exports.createQuestion = asyncHandler(async (req, res) => {
  // 使用集中验证函数

  //validateQuestionRequestBody(...req.body);
  /** 
    // 模拟文件路径处理，为未来中间件预留
    const questionImagePath = files.questionImage ? `/uploads/${Date.now()}.jpg` : '';
    const answerImagePath = files.answerImage ? `/uploads/${Date.now()}.jpg` : '';
  
    // 净化HTML内容
    const cleanHtmlContent = rawHtmlContent ? xss(rawHtmlContent) : '';
  
    // 处理选项（包含图片）
    let processedOptions = [];
    if (options) {
      const optionsArray = JSON.parse(options);
      processedOptions = optionsArray.map((opt, index) => {
        const optionImage = files.optionImages && files.optionImages[index]
          ? `/uploads/${Date.now() + index}.jpg`
          : '';
  
        return {
          label: opt.label || String.fromCharCode(65 + index),
          content: opt.content || '',
          htmlContent: opt.htmlContent || '',
          image: optionImage || '',
          isCorrect: opt.isCorrect || false
        };
      });
    }
  
    // 处理答案（根据题目类型）
    const processedAnswer = type === 'multiple'
      ? processedOptions.filter(opt => opt.isCorrect).map(opt => opt.label)
      : answer;
  */

  // 创建题目对象
  const question = new Question({
    type: req.body.type || '',
    points: Number(req.body.points) || 2,
    difficulty: Number(req.body.difficulty) || 2,
    answer: req.body.answer || '',
    source: req.body.source || [],
    tags: req.body.tags || [],
    htmlAnswer:req.body.htmlAnswer || '',
  });

  if (req.files['image']) {
    question.image = req.files['image'][0].path || '没获取到';
  }

  if(req.files['imageAnswer']){
    question.imageAnswer = req.files['imageAnswer'][0].path || '没获取到';
  }

  try {
    const savedQuestion = await question.save();
    res.status(201).json({
      success: true,
      message: '题目创建成功',
      data: savedQuestion
    });
  } catch (err) {
    console.log('到这里了')
    //处理mongoose 发生的错误
    /**
     * 在model中有如下代码
     * const questionSchema = new mongoose.Schema({
     *    type:{
     *        type:mongoose.Schema.Types.ObjectId,
     *        ref:'QuestionTypes',
     *        required:true,
     *        validate:function(v){
     *            return mongoose.Types.ObjectId.isValid(v);
     *        },
     *        message:'无效的ObjectId'
     *    },
     *    options:{}
     * })
     * 
     * 一开始写这个验证是为了当要保存的内容不是ObjectId时，会抛出这个错误。
     * 预想的是哪怕输入"232你好"，他也能抛出'无效的ObjectId',
     * 可是实际上不是，这个验证仅仅是验证字符串是不是符合ObjectId的格式，
     * 也就是说前提首先得是一个类似ObjectId得字符串它才能走到这一步触发错误；
     * 
     * 因为需要先把字符串尝试着转换为ObjectId,转换成功才能验证是否是有效得ObjectId
     * 
     * 如果输入的额是"232你好"压根就不是ObjectId，那么就在这一层就
     * 被抛出错误了，就到不了mongoose这一层得验证，也就不会抛出'无效的ObjectId'
     * 而是抛出'Cast to ObjectId failed'
     * 
     * 一个save()可能有多个数据的转换和验证，当它遇到一个castError(也即转换错误时)，它不会
     * 立即抛出。而是将所有字段的错收集到顶层ValidationError(是err)的
     * err.errors中。
     * 
     * 想tags是一个数组，且数组第一位发生出错误，那么形式如下
     * "tags.0"{
     *    kind:
     *    value:
     * }
     * 
     * 那么访问这些错误就是err.errors['tags.0']
     *                   err.errors.type
     * 
     * 这些错误可以用
     * err.errors['tags.0'].name形式访问其类型CastError
     * err.errors.type.name     形式访问其类型CastError
     * err.errors[type].name
     * 
     * 那么捕获该错误类型就可以有两种了，
     * 一种是利用if (err.errors['xxx'].name === 'CastError')
     * 一种是利用if (err.name === 'ValidationError') 
     * 
     * 很明晰那第二种更好写，第一种还得获取各种属性
     */

    // 处理Mongoose基础验证错误-ValidationError
    if (err.name === 'ValidationError') {
      /**
       * err.errors是一个对象，没法用数组的.map()方法
       * Object.values()是一个js内置方法，用于以数组的形式返回对象中可以枚举的属性及其值
       * 所以才能用map()方法
       */
      const errors = Object.values(err.errors).map(e => e.path);
      res.status(400);
      throw new Error(`数据验证失败: ${errors.join(', ')} 数值不符合格式`);
    }
    throw err;
  }
});

/**
 * @swagger
 * /api/questions/{id}:
 *   put:
 *     summary: 更新题目
 *     description: 更新题目信息
 *     tags: [Questions]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: 题目ID
 *     requestBody:
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               content:
 *                 type: string
 *               htmlContent:
 *                 type: string
 *               image:
 *                 type: string
 *               type:
 *                 type: string
 *               options:
 *                 type: array
 *                 items:
 *                   type: object
 *               answer:
 *                 type: string
 *               htmlAnswer:
 *                 type: string
 *               imageAnswer:
 *                 type: string
 *               points:
 *                 type: number
 *               difficulty:
 *                 type: number
 *               tags:
 *                 type: array
 *                 items:
 *                   type: string
 *               source:
 *                 type: string
 *     responses:
 *       200:
 *         description: 题目更新成功
 *       400:
 *         description: 无效字段
 *       404:
 *         description: 题目不存在
 *       500:
 *         description: 服务器错误
 */
exports.updateQuestion = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const updateData = { ...req.body };
  // 验证id，其实也有单独的验证函数，但是没有用
  if (!id || !mongoose.Types.ObjectId.isValid(id)) {
    res.status(400);
    throw new Error('无效的类型ID');
  }

  // 使用集中验证函数
  validateQuestionRequestBody(updateData);

  // 处理难度参数
  if (updateData.difficulty) {
    const parsedDifficulty = Number(updateData.difficulty);
    if (isNaN(parsedDifficulty)) {
      res.status(400);
      throw new Error('难度参数必须是数字');
    }
    updateData.difficulty = parsedDifficulty;
  }

  const question = await Question.findByIdAndUpdate(id, updateData, {
    new: true,
    runValidators: true
  });

  if (!question) {
    res.status(404);
    throw new Error('题目不存在');
  }

  res.status(200).json({
    success: true,
    message: '题目更新成功',
    data: question
  });
});

/**
 * @swagger
 * /api/questions/{id}:
 *   delete:
 *     summary: 删除题目
 *     description: 删除指定题目
 *     tags: [Questions]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: 题目ID
 *     responses:
 *       200:
 *         description: 题目删除成功
 *       404:
 *         description: 题目不存在
 *       500:
 *         description: 服务器错误
 */
exports.deleteQuestion = asyncHandler(async (req, res) => {
  const question = await Question.findByIdAndDelete(req.params.id);
  if (!question) {
    res.status(404);
    throw new Error('题目不存在');
  }
  res.status(200).json({ success: true, message: '题目删除成功' });
});

/**
 * @swagger
 * /api/questions/upload:
 *   post:
 *     summary: 上传题目图片
 *     description: 上传题目相关图片并返回图片URL
 *     tags: [Questions]
 *     requestBody:
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             properties:
 *               image:
 *                 type: string
 *                 format: binary
 *     responses:
 *       200:
 *         description: 图片上传成功
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 data:
 *                   type: object
 *                   properties:
 *                     url:
 *                       type: string
 *                     path:
 *                       type: string
 *       400:
 *         description: 未上传文件
 */
exports.uploadImage = asyncHandler(async (req, res) => {
  // 检查文件上传错误（如类型不支持）
  if (req.fileError) {
    res.status(400);
    throw new Error(req.fileError);
  }

  // 检查文件是否存在 - 使用req.files而不是req.file
  // 因为中间件配置为fields模式（支持多文件）
  if (!req.files || !req.files['image'] || req.files['image'].length === 0) {
    res.status(400);
    throw new Error("未检测到有效图片文件");
  }

  const file = req.files['image'][0]; // 获取第一个图片文件
  // console.log(file); // 调试日志

  res.status(200).json({
    success: true,
    data: {
      url: `/uploads/${file.filename}`,  // 访问URL
      path: file.path                   // 服务器存储路径
    }
  });
});
